package com.zzw.utils;


import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.UUID;

import org.apache.commons.collections.FastHashMap;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;


/** 
* @ClassName: BeanUtils 
* @Description:  
* @author ZZW
*  
*/
public class BeanUtils extends org.apache.commons.beanutils.BeanUtils {

	private static final Log logger = LogFactory.getLog(BeanUtils.class);

	private static final DecimalFormat myFormat = new DecimalFormat("#.######################");

	private BeanUtils() {
	}

	
	public static void bindVo(Object vo, Object orig){
		
		try {
			if (vo == null) {
				throw new IllegalArgumentException("No destination bean specified");
			}
			if (orig == null) {
				throw new IllegalArgumentException("No origin bean specified");
			}
			PropertyDescriptor origDescriptors[] = getPropertyDescriptors(orig);

			for (int i = 0; i < origDescriptors.length; i++) {
				try {
					String name = origDescriptors[i].getName();				
					Object value = forceGetProperty(orig, name);
					if(value instanceof Float || value instanceof Double){
						value = myFormat.format(value);
					}
					BeanUtils.copyProperty(vo,name, value);	
				}catch(Exception e){
					logger.error(e);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * @param entityObject
	 * @param formObject
	 */
	public static void copyEntity(Object entityObject, Object formObject){
		PropertyDescriptor origDescriptors[] = getPropertyDescriptors(entityObject);

		for (int i = 0; i < origDescriptors.length; i++) {
			try {
				String name = origDescriptors[i].getName();
				Object value = forceGetProperty(formObject, name);
				forceSetProperty(entityObject, name, value);
			} catch (Exception ee) {
				logger.info("copyEntity---------------:" + ee);
			}
		}
	}
	
	/**
	 * @param formObject
	 * @throws NoSuchFieldException
	 */
	public static void clearForm(Object formObject) throws NoSuchFieldException {
		PropertyDescriptor origDescriptors[] = getPropertyDescriptors(formObject);
		for (int i = 0; i < origDescriptors.length; i++) {
			try {
				String name = origDescriptors[i].getName();
				if (!name.equals("controlId"))
					forceSetProperty(formObject, name, "");
			} catch (Exception ee) {
			}
		}
	}

	public static PropertyDescriptor[] getPropertyDescriptors(Class<?> beanClass) {
		FastHashMap descriptorsCache;
		descriptorsCache = new FastHashMap();
		descriptorsCache.setFast(true);

		if (beanClass == null)
			throw new IllegalArgumentException("No bean class specified");
		PropertyDescriptor descriptors[] = null;
		descriptors = (PropertyDescriptor[]) descriptorsCache.get(beanClass);
		if (descriptors != null)
			return descriptors;
		BeanInfo beanInfo = null;
		try {
			beanInfo = Introspector.getBeanInfo(beanClass);
		} catch (IntrospectionException e) {
			return new PropertyDescriptor[0];
		}
		descriptors = beanInfo.getPropertyDescriptors();
		if (descriptors == null)
			descriptors = new PropertyDescriptor[0];
		descriptorsCache.put(beanClass, descriptors);
		return descriptors;
	}

	/**
	 * @param bean
	 * @return
	 */
	private static PropertyDescriptor[] getPropertyDescriptors(Object bean) {
		if (bean == null)
			throw new IllegalArgumentException("No bean specified");
		else
			return getPropertyDescriptors(bean.getClass());
	}

	
	public static Field getDeclaredField(Object object, String propertyName) throws NoSuchFieldException {
		Assert.notNull(object);
		Assert.hasText(propertyName);
		return getDeclaredField(object.getClass(), propertyName);
	}

	/**
	 * @param clazz
	 * @param propertyName
	 * @return
	 * @throws NoSuchFieldException
	 */
	public static Field getDeclaredField(Class<?> clazz, String propertyName) throws NoSuchFieldException {
		Assert.notNull(clazz);
		Assert.hasText(propertyName);
		for (Class<?> superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
			try {
				return superClass.getDeclaredField(propertyName);
			} catch (NoSuchFieldException e) {}
		}
		throw new NoSuchFieldException("No such field: " + clazz.getName() + '.' + propertyName);
	}

	/**
	 * @param object
	 * @param propertyName
	 * @return
	 * @throws NoSuchFieldException
	 */
	public static Object forceGetProperty(Object object, String propertyName) throws NoSuchFieldException {
		Assert.notNull(object);
		Assert.hasText(propertyName);

		Field field = getDeclaredField(object, propertyName);

		boolean accessible = field.isAccessible();
		field.setAccessible(true);

		Object result = null;
		try {
			result = field.get(object);
			if (result != null && result instanceof java.util.Date)
				return dateToString((java.util.Date) result, "yyyy-MM-dd HH:mm:ss");
			if (result != null && result instanceof java.math.BigDecimal)
				return result.toString();
			else
				return result;

		} catch (IllegalAccessException e) {
			logger.info("error wont' happen");
		}
		field.setAccessible(accessible);
		return result;
	}

	
	/**
	 * @param object
	 * @param propertyName
	 * @param newValue
	 * @throws NoSuchFieldException
	 */
	public static void forceSetProperty(Object object, String propertyName, Object newValue) throws NoSuchFieldException {
		Assert.notNull(object);
		Assert.hasText(propertyName);

		Field field = getDeclaredField(object, propertyName);
		boolean accessible = field.isAccessible();
		field.setAccessible(true);
		try {
			String fieldType = field.getType().getName();
			if(fieldType.toLowerCase().indexOf("timestamp")!=-1){
				newValue = convertSqlTimestamp(newValue.toString());
			}else if (fieldType.toLowerCase().indexOf("date") != -1) {
				if (newValue != null) {
					if (fieldType.equalsIgnoreCase("java.sql.date"))
						newValue = convertSqlDate(newValue.toString());
					else
						newValue = convertUtilDateTime(newValue.toString());
				}
			} else if (fieldType.toLowerCase().indexOf("integer") != -1) {
				if (newValue!=null && StringUtils.isNotBlank(""+newValue))
					newValue = new Integer(newValue.toString());
				else {
					Integer tmp = null;
					newValue = tmp;
				}
			} else if (fieldType.toLowerCase().indexOf("double") != -1) {
				if (newValue!=null && StringUtils.isNotBlank(""+newValue))
					newValue = new Double(newValue.toString());
				else {
					Double tmp = null;
					newValue = tmp;
				}
			} else if (fieldType.toLowerCase().indexOf("bigdecimal") != -1) {
				if (newValue!=null && StringUtils.isNotBlank(""+newValue))
					newValue = new BigDecimal(newValue.toString());
				else {
					BigDecimal tmp = null;
					newValue = tmp;
				}
			} else if (fieldType.toLowerCase().indexOf("long") != -1) {
				if (newValue!=null && StringUtils.isNotBlank(""+newValue))
					newValue = new Long(newValue.toString());
				else {
					Long tmp = null;
					newValue = tmp;
				}
			}
				field.set(object, newValue);
		} catch (IllegalAccessException e) {
			logger.info("Error won't happen");
		}
		field.setAccessible(accessible);
	}

	
	/**
	 * @param object
	 * @param methodName
	 * @param params
	 * @return
	 * @throws NoSuchMethodException
	 */
	public static Object invokePrivateMethod(Object object, String methodName, Object[] params) throws NoSuchMethodException {
		Assert.notNull(object);
		Assert.hasText(methodName);
		@SuppressWarnings("rawtypes")
		Class[] types = new Class[params.length];
		for (int i = 0; i < params.length; i++) {
			types[i] = params[i].getClass();
		}

		Class<?> clazz = object.getClass();
		Method method = null;
		for (Class<?> superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
			try {
				method = superClass.getDeclaredMethod(methodName, types);
				break;
			} catch (NoSuchMethodException e) {
			}
		}

		if (method == null)
			throw new NoSuchMethodException("No Such Method:" + clazz.getName() + methodName);

		boolean accessible = method.isAccessible();
		method.setAccessible(true);
		Object result = null;
		try {
			result = method.invoke(object, params);
		} catch (Exception e) {
			ReflectionUtils.handleReflectionException(e);
		}
		method.setAccessible(accessible);
		return result;
	}
	
	/**
	 * @param object
	 * @param type
	 * @return
	 */
	public static List<Field> getFieldsByType(Object object, Class<?> type) {
		List<Field> list = new ArrayList<Field>();
		Field[] fields = object.getClass().getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			if (fields[i].getType().isAssignableFrom(type)) {
				list.add(fields[i]);
			}
		}
		return list;
	}
	/**
	 * @param type
	 * @param name
	 * @return
	 * @throws NoSuchFieldException
	 */
	public static Class<?> getPropertyType(Class<?> type, String name) throws NoSuchFieldException {
		return getDeclaredField(type, name).getType();
	}
	
	/**
	 * @param type
	 * @param fieldName
	 * @return
	 */
	public static String getGetterName(Class<?> type, String fieldName) {
		Assert.notNull(type, "Type required");
		Assert.hasText(fieldName, "FieldName required");
		if (type.equals(boolean.class)) {
			return "is" + StringUtils.capitalize(fieldName);
		} else {
			return "get" + StringUtils.capitalize(fieldName);
		}
	}
	
	
	
	/**
	 * @return
	 */
	public static String  CREATE_UUID(){
		return UUID.randomUUID().toString().replaceAll("-", "");
	}
	
	
	/**
	 * @param arg
	 * @return
	 */
	public static java.sql.Timestamp convertSqlTimestamp(String arg) {
		java.sql.Timestamp sqldate = null;
		try {
			if (arg != null && !arg.equals("")) {
				if (arg.length() == 10) arg = arg + " 00:00:00";
				DateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss");
				java.util.Date utildate = dateFormat.parse(arg);
				sqldate = new java.sql.Timestamp(utildate.getTime());
			}
		} catch (Exception e) {
			logger.info("convertSqlTimestamp:" + e);
		}
		return sqldate;
	}
	
	
	/**
	 * @param arg
	 * @return
	 */
	public static java.sql.Date convertSqlDate(String arg) {
		java.sql.Date sqldate = null;
		try {
			if (arg != null && !arg.equals("")) {
				DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
				java.util.Date utildate = dateFormat.parse(arg);
				sqldate = new java.sql.Date(utildate.getTime());
			}
		} catch (Exception e) {
			logger.info("convertToSqlDate:" + e);
		}
		return sqldate;
	}
	
	/**
	 * @param arg
	 * @return
	 */
	public static java.util.Date convertUtilDateTime(String arg) {
		java.util.Date sqldate = null;
		try {
			if (arg != null && !arg.equals("")) {
				if (arg.trim().length() == 10) arg = arg + " 00:00:00";
				DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				java.util.Date utildate = dateFormat.parse(arg);
				sqldate = new java.util.Date(utildate.getTime());
			}

		} catch (Exception e) {
			logger.info("convertUtilDateTime:" + e);
		}
		return sqldate;
	}
	
	public static String dateToString(Date date, String formatStyle) {
		try {
			SimpleDateFormat formatter = new SimpleDateFormat(formatStyle);
			return formatter.format(date);
		} catch (Exception e) {
			return date.toString();
		}
	}
	
	/**
	 * @param max
	 * @param min
	 * @return
	 */
	public static int RandomStr(int max,int min){
	    Random random = new Random();
	    int s = random.nextInt(max)%(max-min+1) + min;
	    return s;
	}
	
	
}
